"""
Author        yongfa
Date          2022-07-11 14:48:17
LastEditTime  2023-07-27 19:04:17
LastEditors   yongfa
Description   数据模型
"""
from flask import current_app
from werkzeug.security import generate_password_hash, check_password_hash
from flask_jwt_extended import create_access_token, create_refresh_token, verify_jwt_in_request, get_jwt_identity

from app.extensions import db, redis_client, login_manager, jwt
from app.extensions.sqlalchemy import EntityModel as Base
from app.extensions.utils.enums import UserEnum
from app.extensions.utils import token_key
from app.extensions.exception import TokenRefreshException, TokenLogoutException
from app.modules.v1.permission.models import Role, roles_users
from app.extensions.initialization.user import users_list
from app.modules.web.authentication.util import hash_pass
from flask_login import UserMixin


class User(Base,UserMixin):

    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(128), nullable=False, unique=True, index=True)
    password_hash = db.Column(db.String(128), nullable=False)
    auth = db.Column(db.SmallInteger, index=True, default=UserEnum.COMMON.value, comment='用户类别')

    address = db.Column(db.String(255), comment='地址')
    language = db.Column(db.String(20), comment='语言')
    email = db.Column(db.String(128), comment='邮箱')
    mobile = db.Column(db.String(11), comment='手机号码')
    avatar = db.Column(db.String(255), comment='头像url')
    active = db.Column(db.Boolean(), default=False, nullable=False)
    extend = db.Column(db.String(255), comment='额外备注')

    # role = db.relationship('Role', back_populates='users')
    roles = db.relationship('Role', secondary=roles_users, back_populates='users_list')

    # 微信公众号openid
    openid = db.Column(db.String(32), index=True, comment='支付用户openid')

    # username 唯一
    __table_args__ = (
        db.UniqueConstraint('username', 'delete_time', name='uix_user_username_delete_time'),
    )

    def __init__(self, **kwargs):
        for property, value in kwargs.items():
            # depending on whether value is an iterable or not, we must
            # unpack it's value (when **kwargs is request.form, some values
            # will be a 1-element list)
            if hasattr(value, '__iter__') and not isinstance(value, str):
                # the ,= unpack of a singleton fails PEP8 (travis flake8 test)
                value = value[0]

            if property == 'password':
                value = hash_pass(value)  # we need bytes here (not plain str)

            setattr(self, property, value)

    def __repr__(self):
        return str(self.username)

    @property
    def password(self):
        raise AttributeError('password不是一个可读的属性')

    @password.setter
    def password(self, password='123456'):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    @property
    def exclude_fields(self):
        return super().exclude_fields + ['password_hash']

    def get_roles(self):
        roles = []
        is_super_admin = False
        for role in self.roles:
            roles.append(role.id)
            if role.is_super_admin:
                is_super_admin = True
        return roles, is_super_admin

    def to_dict(self, output_fields=[], exclude_fields=[]):
        res = super().to_dict(output_fields=output_fields, exclude_fields=exclude_fields)
        roles = []
        for role in self.roles:
            roles.append({'id': role.id, 'name': role.name})
        res['roles'] = roles
        return res

    def list_to_dict(self, output_fields=[], exclude_fields=[]):
        res = self.to_dict()
        return res

    @staticmethod
    def init_data():
        for user_info in users_list:
            user = User.get(username=user_info['username'])
            if user is None:
                user = User(username=user_info['username'], password=user_info['password'], auth=user_info['auth'])
                user.roles = Role.query.filter(Role.name.in_(user_info['role_name'])).all()
                user.save()

            else:
                user.password = user_info['password']
                user.auth = user_info['auth']
                user.roles = Role.query.filter(Role.name.in_(user_info['role_name'])).all()
                user.save()

    def get_user_token(self, identity: str = {}, refresh: bool = False):
        """获取user token"""
        expire = current_app.config.get("JWT_TOKEN_EXPIRES")
        if not identity:
            roles, is_super_admin = self.get_roles()
            identity = {
                "id": self.id,
                "auth": self.auth,
                "username": self.username,
                "openid": self.openid,
                "roles": roles,
            }
            if self.auth == UserEnum.ADMIN.value:
                identity['is_super_admin'] = is_super_admin
        token = create_access_token(
            identity=identity
        )
        data = {
            'token': token,
            'expire_time': expire
        }
        if not refresh:
            refresh_expire = current_app.config.get("JWT_REFRESH_TOKEN_EXPIRES")
            key = token_key(identity.get('id'))
            redis_client.set(key, token, refresh_expire)
            data['refresh_token'] = create_refresh_token(identity=identity)
        return data

    def get_refresh_token(self):
        """获取refresh_token"""
        try:
            verify_jwt_in_request(refresh=True)
            identity = get_jwt_identity()
        except Exception:
            raise TokenRefreshException()
        if not redis_client.exists(token_key(identity.get('id'))):
            raise TokenLogoutException()
        user = User.get(id=identity.get('id'))
        token = user.get_user_token(identity, refresh=True)
        return token
    
@login_manager.user_loader
def user_loader(id):
    return User.query.filter_by(id=id).first()


@login_manager.request_loader
def request_loader(request):
    username = request.form.get('username')
    user = User.query.filter_by(username=username).first()
    return user if user else None

@jwt.user_identity_loader
def user_identity_lookup(user):
    return user

@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
    identity = jwt_data["sub"]
    userId=identity.get('id')
    return User.query.get(userId)

